package cn.benma666.sjsj.myutils;

import cn.benma666.constants.UtilConstInstance;
import cn.benma666.dict.Cllx;
import cn.benma666.dict.Jmfs;
import cn.benma666.domain.SysQxYhxx;
import cn.benma666.domain.SysSjglSjdx;
import cn.benma666.exception.BusinessException;
import cn.benma666.exception.MyException;
import cn.benma666.iframe.*;
import cn.benma666.json.JsonUtil;
import cn.benma666.myutils.StringUtil;
import cn.benma666.myutils.WebUtil;
import cn.benma666.sjsj.web.LjqManager;
import cn.benma666.sjsj.web.UserManager;
import cn.benma666.sjzt.SjsjField;
import com.alibaba.druid.util.Utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.beetl.sql.core.DSTransactionManager;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Semaphore;

/**
 * 实现的参数处理等逻辑
 */
public class MyHandlerInterceptor extends BasicObject implements HandlerInterceptor, UtilConstInstance {
    /**
     * 返回前端的对象Key
     */
    public static final String RETURN_BODY = "returnBody";
    /**
     * 并发缓存
     */
    private static final JSONObject bfCache = CacheFactory.use("bfCache");
    /**
     * rest方法映射支持，将http方法映射为本系统的处理类型
     */
    private static final Map<String,Cllx> restMethodYs = new HashMap<>();

    static {
        restMethodYs.put(HttpGet.METHOD_NAME, Cllx.select);
        restMethodYs.put(HttpPost.METHOD_NAME,Cllx.insert);
        restMethodYs.put(HttpPut.METHOD_NAME,Cllx.update);
        restMethodYs.put(HttpDelete.METHOD_NAME,Cllx.delete);
    }
    /**
     * 控制器前执行
     * @param request  请求
     * @param response 返回
     * @param handler  对应的控制器中的方法
     * @return 是否进入控制器
     * @throws Exception 初始化异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        Result r = success("预处理成功");
        MyParams myParams = null;
        try {
            //自定义参数读取逻辑，支持json和普通参数混合传参
            myParams = getParams(request);
            //设置客户端ip
            if(isBlank(myParams.sys().getClientIp())){
                //如果传了ip则采用传的ip
                myParams.sys().setClientIp(WebUtil.getIpAddr(request));
            }
            log.trace("输入参数："+myParams);
            //将原始请求参数克隆备份一份，可能会用到，如：跨网请求需要原始请求参数
            myParams.other().setYsParams(JsonUtil.clone(myParams));
            //设置真实ip
            myParams.sys().setClientRealIp(WebUtil.getRealIpAddr(request));
            myParams.sys().setRequestHost(request.getRemoteHost());
            //将请求对象注入参数对象中
            LjqManager.setHttpServletRequest(request);
            LjqManager.setHttpServletResponse(response);
            LjqManager.setMyParams(myParams);
            //切换语言
            switchLanguage(request, myParams);
            LjqManager.jcxxByParams(myParams,null,false);
            SysQxYhxx user = myParams.user();
            //获取请求中的请求数量
            synchronized (bfCache) {
                Semaphore qqzdqqsl = bfCache.getObject("qqzdqqsl" + user.getToken(),Semaphore.class);
                if(qqzdqqsl==null){
                    qqzdqqsl = new Semaphore(user.getQqbfxz());
                    log.trace("{},{}添加并发控制信号量",user.getYhdm(),user.getToken());
                    bfCache.put("qqzdqqsl" + user.getToken(), qqzdqqsl);
                }
                if (!qqzdqqsl.tryAcquire()) {
                    r = failed("你的并发请求量超过限制：" + user.getQqbfxz());
                } else {
                    //设置参数传入控制层
                    request.setAttribute(KEY_SJDX, myParams.get(KEY_SJDX));
                    log.trace("基础信息处理完成");
                }
            }
        } catch (MyException e) {
            //系统预知异常
            r = e.getResult();
        } catch (Throwable e) {
            //获取基础信息，返回失败信息
            r = failed("获取基础信息异常：" + e.getMessage());
            log.error(r.getMsg(), e);
        }
        if (r.isStatus()) {
            log.trace("放过进入控制器：{}",r);
            //继续进行控制器方法执行
            return true;
        } else {
            LjqManager.sendResult(myParams, r);
            return false;
        }
    }

    /**
     * 控制器后执行
     *
     * @param request      请求
     * @param response     返回
     * @param handler      选择要执行的处理程序，用于类型和/或实例计算
     * @param modelAndView 视图
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler,@Nullable ModelAndView modelAndView) {
        //进行处理完成后的日志记录等操作
        Object r = request.getAttribute(RETURN_BODY);
        MyParams p = LjqManager.getMyParams();
        if(DSTransactionManager.inTrans()){
            log.error("业务处理结束，但事务未关闭");
            try {
                DSTransactionManager.rollback();
            } catch (SQLException e) {
                log.debug("事务回滚失败",e);
            }
        }
        log.trace("返回内容：" + r);
        if(r==null){
            return;
        }
        MyParams myParams = null;
        Result res;
        if(r instanceof Result){
            res = (Result) r;
        }else{
            res = success("处理完成",r);
        }
        if(p!=null){
            myParams = p;
            SysQxYhxx user = myParams.user();
            //请求结束，减少一个请求数
            if(user!=null){
                synchronized (bfCache){
                    Semaphore qqzdqqsl = bfCache.getObject("qqzdqqsl" + user.getToken(), Semaphore.class);
                    if(qqzdqqsl!=null){
                        qqzdqqsl.release();
                        if(qqzdqqsl.availablePermits()>=user.getQqbfxz()){
                            bfCache.remove("qqzdqqsl" + user.getToken());
                            log.trace("{}，{}移除并发控制信号量",user.getYhdm(),user.getToken());
                        }
                    }else {
                        log.warn("{}，{}没有找到并发控制信号量",user.getYhdm(),user.getToken());
                    }
                }
            }
        }
        LjqManager.sendResult(myParams ,res);
        //本次请求结束，移除参数线程变量
        LjqManager.setMyParams(null);
    }

    /**
     * 切换国际化语言
     *
     * @param request  请求
     * @param myParams 参数
     */
    private void switchLanguage(HttpServletRequest request, MyParams myParams) {
        Object language = myParams.sys().getLanguage();
        if (language != null) {
            request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,
                    Locale.forLanguageTag(language.toString()));
            log.info(Msg.msg("switch.language", language));
        }
    }

    /**
     * 功能描述:通过request来获取到json数据<br/>
     *
     * @param request 请求对象
     * @return 请求参数JSONObject
     * @throws IOException 参数处理异常
     */
    private MyParams getParams(HttpServletRequest request) throws IOException {
        MyParams myParams = null;
        //请求参数为json时
        String ct = request.getContentType();
        if (ct!=null&&ct.contains(MediaType.TEXT_PLAIN_VALUE)) {
            String content = Utils.read(request.getInputStream());
            //加密场景
            try {
                content = StringUtil.decodeing(content, Jmfs.sm2,null,
                        Conf.getUtilConfig().getData().getPrivateKey());
            }catch (Throwable t){
                throw new BusinessException("请求参数解密失败：【"+content+"】,请确认参数加密是否正确",t);
            }
            // 获取输入流读取配置，与默认配置合并
            myParams = MyParams.parseObject(content, Feature.OrderedField);
        }else if (ct!=null&&ct.contains(MediaType.APPLICATION_JSON_VALUE)) {
            String content = Utils.read(request.getInputStream());
            // 获取输入流读取配置，与默认配置合并
            myParams = MyParams.parseObject(content, Feature.OrderedField);
        }
        if(myParams==null){
            myParams = new MyParams(true);
        }
        //支持在header中传入sys相关参数
        Enumeration<String> hn = request.getHeaderNames();
        Map<String,String> h = new HashMap<>();
        while (hn.hasMoreElements()){
            String pk = hn.nextElement();
            h.put(pk,request.getHeader(pk));
            //优先级最低，存在则不再覆盖
            if(pk.startsWith("sys-")&&!myParams.containsKey("$."+pk.replace("-","."))){
                if (!myParams.set("$." + pk.replace("-","."), request.getHeader(pk))) {
                    log.warn(pk + "参数设置失败：" + request.getHeader(pk));
                }
            }
        }
        log.trace("请求头:{}", JSON.toJSONString(h));
        //合并以普通请求参数传入的参数
        Map<String, String[]> pm;
        try {
            pm = request.getParameterMap();
        }catch (Throwable e){
            throw new BusinessException("获取请求的get参数失败，请求检查你的请求信息："+e.getMessage(),e);
        }
        for (String pk : pm.keySet()) {
            String[] va = pm.get(pk);
            try{
                if (va.length > 0&&StringUtil.isNotBlank(va[0])) {
                    if(pk.contains(".")){
                        if (!myParams.set("$." + pk, va[0])) {
                            log.warn(pk + "参数设置失败：" + va[0]);
                        }
                    }else if(va[0].startsWith("{")){
                        //值为json字符串
                        if (!myParams.set("$." + pk, JSON.parse(va[0]))) {
                            log.warn(pk + "参数设置失败：" + va[0]);
                        }
                    }else {
                        //没有带路径的参数都放到yobj中
                        if (!myParams.set("$.yobj." + pk, va[0])) {
                            log.warn(pk + "参数设置失败：" + va[0]);
                        }
                    }
                }
            }catch (Throwable e){
                throw new BusinessException("get参数处理异常："+pk+"="+va[0]+">"+e.getMessage(),e);
            }
        }
        SysSjglSjdx sjdx = myParams.sjdx();
        //请求路径
        String sp = request.getServletPath();
        if(sp.startsWith("/custom")){
            //定制场景支持
            int idx=sp.lastIndexOf("/");
            //设置处理了类型
            myParams.sys().setCllx(sp.substring(idx+1));
            //设置控制器
            myParams.sys().setController(sp.substring(0,idx));
        }
        if(sp.startsWith("/csjdx")){
            //数据对象定制控制器场景支持
            int idx=sp.lastIndexOf("/");
            //设置处理了类型
            myParams.sys().setCllx(sp.substring(idx+1));
            //设置控制器
            sjdx.setDxdm(sp.substring("/custom".length(),idx));
        }
        //合并处理路径参数
        Map<String,String> pathValMap = (Map<String,String>)request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.uriTemplateVariables");
        if(pathValMap!=null){
            if(pathValMap.containsKey(SjsjField.dxdm.name())){
                //对象代码支持小写中划线
                sjdx.setDxdm(pathValMap.get(SjsjField.dxdm.name()));
            }
            if(pathValMap.containsKey(SjsjField.authCode.name())){
                //对象代码支持小写中划线
                myParams.sys().setAuthCode(pathValMap.get(SjsjField.authCode.name()));
            }
            if(pathValMap.containsKey(SjsjField.cllx.name())){
                myParams.sys().setCllx(pathValMap.get(SjsjField.cllx.name()));
            }
            if(pathValMap.containsKey("key")){
                myParams.sys().setSearchKey(pathValMap.get("key"));
            }
        }
        String dxdm = sjdx.getDxdm();
        if(!isBlank(dxdm)){
            //对象代码统一为大写
            sjdx.setDxdm(dxdm.replace("-","_").toUpperCase());
        }
//        if(isBlank(myParams.getString($_SYS_CLLX))){
//            //能锁定数据对象，但没有传处理类型，采用请求方法映射设置处理类型，从而实现对rest接口的支持
//            myParams.set($_SYS_CLLX,restMethodYs.get(request.getMethod().toUpperCase()));
//        }
        getToken(myParams,request);
        return myParams;
    }
    /**
     * 获取权限id
     */
    private void getToken(MyParams myParams,HttpServletRequest req) {
        if (StringUtil.isNotBlank(myParams.sys().getToken())) {
            return;
        }
        if(req==null){
            return;
        }
        //先取header中
        String token = req.getHeader(UserManager.TOKEN);
        if (StringUtil.isBlank(token)) {
            //再取常规浏览器会话id
            token = req.getSession().getId();
        }
        //常规参数中没有设置权限认证key
        myParams.sys().setToken(token);
    }
}
